package com.c5p1ng.groovy.demo;

import cn.hutool.crypto.digest.DigestUtil;
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyObject;
import groovy.lang.GroovyShell;
import groovy.lang.Script;
import groovy.util.GroovyScriptEngine;
import org.codehaus.groovy.runtime.InvokerHelper;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class GroovyDemo1 {
    private static GroovyShell groovyShell = new GroovyShell();

    private static Map<String, Script> scriptCache = new ConcurrentHashMap<>();

    private static <T> T invoke(String scriptText, String function, Object... objects) throws Exception {
        Script script;
        String cacheKey = DigestUtil.md5Hex(scriptText);

        if (scriptCache.containsKey(cacheKey)) {
            script = scriptCache.get(cacheKey);
        } else {
            script = groovyShell.parse(scriptText);
            scriptCache.put(cacheKey, script);
        }

        return (T) InvokerHelper.invokeMethod(script, function, objects);
    }

    public static void main(String[] args) throws Exception {
        String groovyScript = "def cal(int a, int b){\n" +
                "    return a+b\n" +
                "}";

        Object[] param = {8, 7};

        function1(groovyScript, param);

        int result = invoke(groovyScript, "cal", param);
        System.out.println(result);

        GroovyScriptEngine engine = new GroovyScriptEngine(groovyScript);
    }

    private static void function1(String groovyScript, Object[] param) {
        GroovyClassLoader classLoader = new GroovyClassLoader();
        /**
         * groovy每执行一次脚本，都会生成一个脚本的class对象，
         * 这个class对象的名字由 “script” + System.currentTimeMillis() +
         * Math.abs(text.hashCode()组成，
         *
         * 问题：groovy每执行一次脚本都会new一个InnerLoader去加载这个对象，
         * 我们可以推测：InnerLoader和脚本对象都无法在fullGC的时候被回收，
         * 因此运行一段时间后将PERM占满，一直触发fullGC。
         */
        Class groovyClass = classLoader.parseClass(groovyScript);
        try {
            GroovyObject groovyObject = (GroovyObject) groovyClass.newInstance();
            int result = (int) groovyObject.invokeMethod("cal", param);
            System.out.println(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
